﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace Sustainalytics.Utils
{
    using System.Reflection;

    public static class ExpressionsUtils
    {
        public static void SetPropertyValue<TK, TU>(this TK target, Expression<Func<TK, TU>> memberLamda, TU value)
        {
            var bits = GetPropertyName(memberLamda).Split('.');
            object newTarget = target;
            for (int i = 0; i < bits.Length - 1; i++)
            {
                PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
                newTarget = propertyToGet.GetValue(newTarget, null);
            }

            PropertyInfo propertyToSet = newTarget.GetType().GetProperty(bits.Last());
            propertyToSet.SetValue(newTarget, value, null);
        }

        public static Expression<Func<T, object>> GetExpression<T>(this string orderBy)
        {
            Type t = typeof(T);
            ParameterExpression parameter = Expression.Parameter(t);
            Expression expression = parameter;

            var properties = orderBy.Split('.');

            for (int i = 0; i < properties.Count(); i++)
            {
                expression = Expression.Property(expression, t, properties.ElementAt(i));
                t = expression.Type;
            }

            var lambdaExpression = Expression.Lambda<Func<T, object>>(expression, parameter);
            return lambdaExpression;
        }

        public static string GetPropertyName<T, TK>(this Expression<Func<T, TK>> expr)
        {
            var path = new StringBuilder();
            MemberExpression memberExpression = GetMemberExpression(expr);
            do
            {
                if (path.Length > 0)
                {
                    path.Insert(0, ".");
                }
                path.Insert(0, memberExpression.Member.Name);
                memberExpression = GetMemberExpression(memberExpression.Expression);
            }
            while (memberExpression != null);
            return path.ToString();
        }

        private static MemberExpression GetMemberExpression(Expression expression)
        {
            if (expression is MemberExpression)
            {
                return (MemberExpression)expression;
            }
            else if (expression is LambdaExpression)
            {
                var lambdaExpression = expression as LambdaExpression;
                if (lambdaExpression.Body is MemberExpression)
                {
                    return (MemberExpression) lambdaExpression.Body;
                }
                else if (lambdaExpression.Body is UnaryExpression)
                {
                    return ((MemberExpression) ((UnaryExpression) lambdaExpression.Body).Operand);
                }
               // else if (lambdaExpression.Body is MethodBinaryExpression)
                    // ((MethodBinaryExpression)lambdaExpression.Body).Left is MemberExpression
            }
            return null;
        }
    }
}

